﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using Xunit;
using SimpleInjector;
using MongoDB.Bson;
using MongoDB.Driver;
using Sustainalytics.Synchronization.Interface;
using Sustainalytics.Entities.Governance;
using Sustainalytics.MongoDB.Extensions;

namespace Sustainalytics.Governance.Computing
{
    public class GovernanceComputationIntegrationTests
    {
        protected Container SimpleInjectorContainer;
        private readonly GovernanceComputationEngine _computationEngine;
        private static ConcurrentQueue<string> _log;

        readonly SyncContext _syncContext = new SyncContext()
        {
            GAConnectionStrings = new Dictionary<string, string>
            {
                { "ClientDW", "mongodb://localhost:27017/ClientDW_B" },
                { "GovernanceDW", "mongodb://localhost:27017/GovernanceDW_B" },
                { "GovernanceInputFile", "mongodb://54.154.132.43:27017/GovernanceInputFile" },
                //{ "GovernanceDW", "mongodb://54.72.160.155:27017/GovernanceDW_A" },
                //{ "GovernanceInputFile", "mongodb://54.72.160.155:27017/GovernanceInputFile" }
            },
            Report = new ConcurrentBag<string>()
        };

        public GovernanceComputationIntegrationTests()
        {
            SimpleInjectorContainer = SimpleInjectorConfig.Register(new SyncContextLogger(_syncContext), _syncContext.GAConnectionStrings);

            _computationEngine = SimpleInjectorContainer.GetInstance<GovernanceComputationEngine>();

            _log = new ConcurrentQueue<string>();
        }


        [Fact]
        public void Start()
        {
            _computationEngine.Compute();

            var logging = _computationEngine.Log;

        }

        [Fact]
        public void CheckComputationResults()
        {
            const string _localDwConnectionString = "mongodb://localhost:27017/GovernanceDW_B";
            const string _remoteDwConnectionString = "mongodb://54.154.132.43:27017/GovernanceDW_B";

            var localCollection = MongoFactory.GetCollectionFromDatabase<GovernanceComputationResult>(_localDwConnectionString);
            var remoteCollection = MongoFactory.GetCollectionFromDatabase<GovernanceComputationResult>(_remoteDwConnectionString);

            var localResults = localCollection.Find(new BsonDocument()).ToList();
            var remoteResults = remoteCollection.Find(new BsonDocument()).ToList().ToDictionary(r => r.Id);

            foreach (var localResult in localResults)
            {
                var remoteResult = remoteResults.ContainsKey(localResult.Id)
                    ? remoteResults[localResult.Id]
                    : null;

                if (remoteResult == null)
                    continue; //throw new Exception("no local result can be found!");

                foreach (var localIssue in localResult.IssuesPerformance)
                {
                    var remoteIssue = remoteResult.IssuesPerformance.Where(i => i.Number == localIssue.Number).SingleOrDefault();

                    if (remoteIssue == null)
                        throw new Exception("no issue can be found!");


                    if (Math.Round(localIssue.Score, 2) != Math.Round(remoteIssue.Score, 2))
                    {
                        var diff = Math.Round(Math.Abs(localIssue.Score - remoteIssue.Score), 5);
                        _log.Enqueue($"is a different score {diff} for issue: {localIssue.Number}, company id: {localResult.Id}");
                    }
                    if (Math.Round(localIssue.Weight, 2) != Math.Round(remoteIssue.Weight, 2))
                    {
                        _log.Enqueue($"is a different weight for issue: {localIssue.Number}, company id: {localResult.Id}");
                    }

                    foreach (var remotePerformance in remoteIssue.Performances)
                    {
                        var localPerformance = localIssue.Performances.Where(x => x.PerformanceType == remotePerformance.PerformanceType).SingleOrDefault();

                        if (localPerformance == null)
                            throw new Exception("no performance can be found!");

                        if (localPerformance.Rank != remotePerformance.Rank)
                        {
                            //_log.Enqueue($"is a different Rank for {localPerformance.PerformanceType}, Issue: {localIssue.Number}, company id: {localResult.Id}");
                        }
                    }

                    if (localIssue.Assessment != remoteIssue.Assessment)
                    {
                        _log.Enqueue($"is a different Assessment {localIssue.Assessment} ({(int)localIssue.Score}), remote: {remoteIssue.Assessment} ({remoteIssue.Score}), for Issue: {localIssue.Number}, company id: {localResult.Id}");
                    }
                }

                if (Math.Round(localResult.OverallPerformance.Score, 3) != Math.Round(remoteResult.OverallPerformance.Score, 3))
                {
                    var diff = Math.Round(Math.Abs(localResult.OverallPerformance.Score - remoteResult.OverallPerformance.Score), 3);

                    _log.Enqueue($"is a different score for company id: {remoteResult.Id}");
                }
            }

            var log = _log.ToString();
        }

    }
}
